package errs

import (
	"fmt"
	"strings"

	"github.com/pkg/errors"
)

type APICoder interface {
	Code() int
	Message() string
	Reference() string
	Error() string
}

type apiCode struct {
	code     int
	msg      string
	ref      string
	extraMsg string
}

func (a *apiCode) Code() int {
	return a.code
}

func (a *apiCode) Message() string {
	if a.extraMsg != "" {
		return fmt.Sprintf("%s(%s)", a.msg, a.extraMsg)
	}
	return a.msg
}

func (a *apiCode) Reference() string {
	return a.ref
}

func (a *apiCode) Error() string {
	return fmt.Sprintf("%d: %s", a.code, a.Message())
}

func New(code int, message string, reference ...string) APICoder {
	ref := ""
	if len(reference) > 0 {
		ref = reference[0]
	}
	return &apiCode{
		code: code,
		msg:  message,
		ref:  ref,
	}
}

func Wrap(err error, msg string) error {
	return errors.WithStack(errors.Wrap(err, msg))
}

func Cause(err error) error {
	return errors.Cause(err)
}

func StackTrace(err error) string {
	if traceErr, ok := err.(interface{ StackTrace() errors.StackTrace }); ok {
		return fmt.Sprintf("%+v", traceErr.StackTrace())
	}
	return ""
}

func AsAPICoder(err error) APICoder {
	errMsg := err.Error()
	extraMsg := ""
	if strings.Contains(strings.ToLower(errMsg), "panic") {
		lines := strings.Split(errMsg, "\n")
		if len(lines) > 0 {
			extraMsg = lines[0]
		}
	}

	var apiErr APICoder
	if errors.As(err, &apiErr) {
		var ac *apiCode
		if errors.As(apiErr, &ac) {
			ac.extraMsg = extraMsg
		}
		return apiErr
	}
	return nil
}
